package alog

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
)

var (
	info     *log.Logger // 重要的信息
	loglevel level       //日志级别
)

type level struct {
	l int
}

func (self *level) Set(l string) error {
	switch l {
	case "INFO":
		self.l = INFO
	case "WARN":
		self.l = WARN
	case "ERROR":
		self.l = ERROR
	default:
		log.Printf("Invalid log level.")
		return errors.New("Invalid log level.")
	}
	return nil
}

func (self *level) String() string {
	switch self.l {
	case INFO:
		return "INFO"
	case WARN:
		return "WARN"
	case ERROR:
		return "ERROR"
	case CRITICAL:
		return "CRITCAL"
	default:
		return "IVALID LOG LEVEL."
	}

}

const (
	INFO = iota
	WARN
	ERROR
	CRITICAL
)

var LogFile *os.File

var w1 io.Writer
var w2 io.Writer

type LogConfig struct {
	path     *string
	stdout   *bool
	stderr   *bool
	loglevel level
}

var mylog LogConfig

func init() {
	mylog.path = flag.String("log_dir", "", "The log output file")
	mylog.stdout = flag.Bool("to_stdout", true, "If log output to stdout")
	mylog.stderr = flag.Bool("to_stderr", true, "If error log  output to stderr")
	flag.Var(&mylog.loglevel, "log_devel", "Log output level,[INFO|WARN| ERROR],(default:INFO)")
}

func InitLog(conflog string) {
	if conflog != "" {
		*mylog.path = *mylog.path + conflog
	}
	file, err := os.OpenFile(*mylog.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal("Failed to open error log file:", err)
	}
	LogFile = file

	if *mylog.stdout && *mylog.stderr {
		w1 = io.MultiWriter(file, os.Stdout)
		w2 = io.MultiWriter(file, os.Stderr)
	} else if *mylog.stderr {
		w1 = io.MultiWriter(file)
		w2 = io.MultiWriter(file, os.Stderr)
	} else if *mylog.stdout {
		w1 = io.MultiWriter(file, os.Stdout)
		w2 = io.MultiWriter(file)
	} else {
		w1 = io.MultiWriter(file)
		w2 = io.MultiWriter(file)
	}

	flag := log.LstdFlags

	info = log.New(w1, "INFO ", flag)

}

func Infof(format string, v ...interface{}) {
	info.SetOutput(w1)
	_, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d: ", f, line)
		info.SetPrefix("INFO " + prefix)
	}
	if mylog.loglevel.l <= INFO {
		info.Print(fmt.Sprintf(format,v...))
	}
}

func Warnf(format string, v ...interface{}) {
	info.SetOutput(w1)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("WARN " + prefix)
	}
	if mylog.loglevel.l <= WARN {
		info.Print(fmt.Sprintf(format,v...))
	}
}

func Errorf(format string, v ...interface{}) {
	info.SetOutput(w2)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("ERROR " + prefix)
	}
	if mylog.loglevel.l <= ERROR {
		info.Print(fmt.Sprintf(format,v...))
	}
}

func Critf(format string, v ...interface{}) {
	info.SetOutput(w2)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("CRIT " + prefix)
	}
	info.Print(fmt.Sprintf(format,v...))
}

func Info(format string) {
	info.SetOutput(w1)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("INFO " + prefix)
	}
	if mylog.loglevel.l <= INFO {
		info.Print(format)
	}
}

func Warn(format string) {
	info.SetOutput(w1)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("WARN " + prefix)
	}
	if mylog.loglevel.l <= WARN {
		info.Print(format)
	}
}

func Error(format string) {
	info.SetOutput(w2)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("ERROR " + prefix)
	}
	if mylog.loglevel.l <= ERROR {
		info.Print(format)
	}
}

func Crit(format string) {
	info.SetOutput(w2)
	funcName, f, line, ok := runtime.Caller(1)
	if ok {
		prefix := fmt.Sprintf("%s:%d:%s ", f, line, runtime.FuncForPC(funcName).Name())
		info.SetPrefix("CRIT " + prefix)
	}
	info.Fatal(format)
}

